#include "Threading/U2DefaultWorkQueueStandard.h"
#include "U2LogManager.h"
#include "U2Root.h"
#include "U2RenderSystem.h"


U2EG_NAMESPACE_USING


//---------------------------------------------------------------------
U2DefaultWorkQueue::U2DefaultWorkQueue(const U2String& name)
: U2DefaultWorkQueueBase(name)
{
}
//---------------------------------------------------------------------
U2DefaultWorkQueue::~U2DefaultWorkQueue()
{
	shutdown();
}
//---------------------------------------------------------------------
void U2DefaultWorkQueue::startup(bool forceRestart)
{
	if (mIsRunning)
	{
		if (forceRestart)
			shutdown();
		else
			return;
	}

	mShuttingDown = false;

	mWorkerFunc = U2_NEW_T(WorkerFunc(this), MEMCATEGORY_GENERAL);

	U2LogManager::getSingleton().stream() <<
		"U2DefaultWorkQueue('" << mName << "') initialising on thread " <<
#if U2_THREAD_SUPPORT
		U2_THREAD_CURRENT_ID
#else
		"main"
#endif
		<< ".";

#if U2_THREAD_SUPPORT
	if (mWorkerRenderSystemAccess)
		U2Root::getSingleton().getRenderSystem()->preExtraThreadsStarted();

	mNumThreadsRegisteredWithRS = 0;
	for (uint8 i = 0; i < mWorkerThreadCount; ++i)
	{
		U2_THREAD_CREATE(t, *mWorkerFunc);
		mWorkers.push_back(t);
	}

	if (mWorkerRenderSystemAccess)
	{
		U2_LOCK_MUTEX_NAMED(mInitMutex, initLock)
		// have to wait until all threads are registered with the render system
		while (mNumThreadsRegisteredWithRS < mWorkerThreadCount)
			U2_THREAD_WAIT(mInitSync, mInitMutex, initLock);

		U2Root::getSingleton().getRenderSystem()->postExtraThreadsStarted();

	}
#endif

	mIsRunning = true;
}
//---------------------------------------------------------------------
void U2DefaultWorkQueue::notifyThreadRegistered()
{
	U2_LOCK_MUTEX(mInitMutex)

	++mNumThreadsRegisteredWithRS;

	// wake up main thread
	U2_THREAD_NOTIFY_ALL(mInitSync);

}
//---------------------------------------------------------------------
void U2DefaultWorkQueue::shutdown()
{
	if( !mIsRunning )
		return;

	U2LogManager::getSingleton().stream() <<
		"U2DefaultWorkQueue('" << mName << "') shutting down on thread " <<
#if U2_THREAD_SUPPORT
		U2_THREAD_CURRENT_ID
#else
		"main"
#endif
		<< ".";

	mShuttingDown = true;
	abortAllRequests();
#if U2_THREAD_SUPPORT
	// wake all threads (they should check shutting down as first thing after wait)
	U2_THREAD_NOTIFY_ALL(mRequestCondition)

	// all our threads should have been woken now, so join
	for (WorkerThreadList::iterator i = mWorkers.begin(); i != mWorkers.end(); ++i)
	{
		(*i)->join();
		U2_THREAD_DESTROY(*i);
	}
	mWorkers.clear();
#endif

	if (mWorkerFunc)
	{
		U2_DELETE_T(mWorkerFunc, WorkerFunc, MEMCATEGORY_GENERAL);
		mWorkerFunc = 0;
	}


	mIsRunning = false;
}
//---------------------------------------------------------------------
void U2DefaultWorkQueue::notifyWorkers()
{
	// wake up waiting thread
	U2_THREAD_NOTIFY_ONE(mRequestCondition)
}

//---------------------------------------------------------------------
void U2DefaultWorkQueue::waitForNextRequest()
{
#if U2_THREAD_SUPPORT
	// Lock; note that U2_THREAD_WAIT will free the lock
	U2_LOCK_MUTEX_NAMED(mRequestMutex, queueLock);
	if (mRequestQueue.empty())
	{
		// frees lock and suspends the thread
		U2_THREAD_WAIT(mRequestCondition, mRequestMutex, queueLock);
	}
	// When we get back here, it's because we've been notified 
	// and thus the thread has been woken up. Lock has also been
	// re-acquired, but we won't use it. It's safe to try processing and fail
	// if another thread has got in first and grabbed the request
#endif

}
//---------------------------------------------------------------------
void U2DefaultWorkQueue::_threadMain()
{
	// default worker thread
#if U2_THREAD_SUPPORT
	U2LogManager::getSingleton().stream() << 
		"U2DefaultWorkQueue('" << getName() << "')::WorkerFunc - thread " 
		<< U2_THREAD_CURRENT_ID << " starting.";

	// Initialise the thread for RS if necessary
	if (mWorkerRenderSystemAccess)
	{
		U2Root::getSingleton().getRenderSystem()->registerThread();
		notifyThreadRegistered();
	}

	// Spin forever until we're told to shut down
	while (!isShuttingDown())
	{
		waitForNextRequest();
		_processNextRequest();
	}

	U2LogManager::getSingleton().stream() << 
		"U2DefaultWorkQueue('" << getName() << "')::WorkerFunc - thread " 
		<< U2_THREAD_CURRENT_ID << " stopped.";
#endif
}

